#lang racket

(define (factorial n)
  (match n
    [0 1]
    [_ (* n (factorial (- n 1)))]))

; (factorial 10)


(define/match (factorial:2 n)
  [(0) 1]
  [(_) (* n (factorial:2 (- n 1)))])

; (factorial:2 10)

(define (f l)
  (match l
    [(list) 'empty-list]            ; Matches the empty list
    [(list 1 2) 'list-1-2]        ; Matches a list with exactly 1 and 2
    [(list x y) (cons x y)]   ; Matches a list with any two elements
    [(list h t ...) t]))   ; Matches a nonempty list

(require rackunit)
(check-equal? (f (list)) 'empty-list)
(check-equal? (f (list 1)) (list))
(check-equal? (f (list 1 2 3 4)) (list 2 3 4))
(check-equal? (f (list 1 2)) 'list-1-2)
(check-equal? (f (list 2 3)) (cons 2 3))


(define (map f l)
  (cond [(empty? l) l]
        [else (cons (f (first l)) (map f (rest l)))]))

(define (map:2 f l)
  (match l
    [(list) l]
    [_ (cons (f (first l)) (map:2 f (rest l)))]
  ))

(define (plus1 x) (+ x 1))

(check-equal? (map plus1 (list 30 40 50)) (map:2 plus1 (list 30 40 50)))


(define (map:3 f l)
  (match l
    [(list) l]
    [(list h t ...) (cons (f h) (map:3 f t))]))


(check-equal? (map plus1 (list 30 40 50)) (map:3 plus1 (list 30 40 50)))

(define (member x l)
  (cond
    [(empty? l) #f]
    [(equal? (first l) x) #t]
    [else (member x (rest l))]))

(define (member:2 x l)
  (match l
    [(list) #f]
    [(list h _ ...) #:when (equal? h x)  #t]
    [(list _ t ...) (member:2 x t)]))


(check-equal? (member:2 10 (list 1 2 3 5)) #f)

(check-equal? (member:2 10 (list 1 2 10 5)) #t)

(struct foo (bar baz))
(define (g x)
  (match x
    [(foo a b) (+ a b)]))
(check-equal? (g (foo 1 2)) (+ 1 2))













;